use rand::{rngs::StdRng, Rng, SeedableRng};

use crate::renderer::firework::{Drawable, Firework};

pub struct StateMachine {
  rng: StdRng,
  fireworks: Vec<Firework>,
}

impl StateMachine {
  pub fn new(seed: u64, term_height: u16) -> Self {
    let rng = StdRng::seed_from_u64(seed);
    StateMachine {
      fireworks: vec![Firework::new(rng.clone(), term_height)], // always start with 1 :)
      rng,
    }
  }

  pub fn tick(&mut self, term_height: u16) -> Vec<&dyn Drawable> {
    self.gc_old_fireworks();
    self.maybe_add_firework(term_height);
    self.update_fireworks();
    self.collect_drawables()
  }

  fn update_fireworks(&mut self) {
    for firework in self.fireworks.iter_mut() {
      firework.advance();
    }
  }

  fn collect_drawables(&self) -> Vec<&dyn Drawable> {
    let mut drawables = Vec::new();

    for firework in self.fireworks.iter() {
      drawables.extend(&firework.drawables());
    }

    drawables
  }

  fn maybe_add_firework(&mut self, term_height: u16) {
    if self.should_gen_firework() {
      self
        .fireworks
        .push(Firework::new(self.rng.clone(), term_height));
    }
  }

  fn should_gen_firework(&mut self) -> bool {
    self.rng.gen_range(0, 100) <= 10
  }

  fn gc_old_fireworks(&mut self) {
    let gcable_fireworks = self.gcable_firework_idx();
    if gcable_fireworks > 0 {
      self.fireworks.drain(..gcable_fireworks);
    }
  }

  fn gcable_firework_idx(&self) -> usize {
    let mut gcable_count = 0;
    for firework in self.fireworks.iter() {
      if firework.extinguished() {
        gcable_count += 1;
      } else {
        break;
      }
    }
    gcable_count
  }
}
